namespace Creek.UI.Tabstrip.Control
{
    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Windows.Forms;

    using Creek.UI.Tabstrip.BaseClasses;
    using Creek.UI.Tabstrip.Design;

    [Designer(typeof(FATabStripDesigner))]
    [DefaultEvent("TabStripItemSelectionChanged")]
    [DefaultProperty("Items")]
    [ToolboxItem(true)]
    [ToolboxBitmap("FATabStrip.bmp")]
    public class FATabStrip : BaseStyledPanel, ISupportInitialize, IDisposable
    {
        #region Static Fields

        internal static int PreferredWidth = 350;

        internal static int PreferredHeight = 200;

        #endregion

        #region Constants

        private const int DEF_HEADER_HEIGHT = 19;

        private const int DEF_GLYPH_WIDTH = 40;

        private int DEF_START_POS = 10;

        #endregion

        #region Events

        public event TabStripItemClosingHandler TabStripItemClosing;

        public event TabStripItemChangedHandler TabStripItemSelectionChanged;

        public event HandledEventHandler MenuItemsLoading;

        public event EventHandler MenuItemsLoaded;

        public event EventHandler TabStripItemClosed;

        #endregion

        #region Fields

        private static readonly Font defaultFont = new Font("Tahoma", 8.25f, FontStyle.Regular);

        private readonly FATabStripCloseButton closeButton;

        private readonly FATabStripItemCollection items;

        private readonly ContextMenuStrip menu;

        private readonly FATabStripMenuGlyph menuGlyph;

        private readonly StringFormat sf;

        private bool alwaysShowClose = true;

        private bool alwaysShowMenuGlyph = true;

        private bool isIniting;

        private bool menuOpen;

        private FATabStripItem selectedItem;

        private Rectangle stripButtonRect = Rectangle.Empty;

        #endregion

        #region Methods

        #region Public

        /// <summary>
        /// 	Returns hit test results
        /// </summary>
        /// <param name="pt"> </param>
        /// <returns> </returns>
        public HitTestResult HitTest(Point pt)
        {
            if (this.closeButton.Bounds.Contains(pt))
            {
                return HitTestResult.CloseButton;
            }

            if (this.menuGlyph.Bounds.Contains(pt))
            {
                return HitTestResult.MenuGlyph;
            }

            if (this.GetTabItemByPoint(pt) != null)
            {
                return HitTestResult.TabItem;
            }

            //No other result is available.
            return HitTestResult.None;
        }

        /// <summary>
        /// 	Add a <see cref="FATabStripItem" /> to this control without selecting it.
        /// </summary>
        /// <param name="tabItem"> </param>
        public void AddTab(FATabStripItem tabItem)
        {
            this.AddTab(tabItem, false);
        }

        /// <summary>
        /// 	Add a <see cref="FATabStripItem" /> to this control.
        /// 	User can make the currently selected item or not.
        /// </summary>
        /// <param name="tabItem"> </param>
        public void AddTab(FATabStripItem tabItem, bool autoSelect)
        {
            tabItem.Dock = DockStyle.Fill;
            this.Items.Add(tabItem);

            if ((autoSelect && tabItem.Visible) || (tabItem.Visible && this.Items.DrawnCount < 1))
            {
                this.SelectedItem = tabItem;
                this.SelectItem(tabItem);
            }
        }

        /// <summary>
        /// 	Remove a <see cref="FATabStripItem" /> from this control.
        /// </summary>
        /// <param name="tabItem"> </param>
        public void RemoveTab(FATabStripItem tabItem)
        {
            int tabIndex = this.Items.IndexOf(tabItem);

            if (tabIndex >= 0)
            {
                this.UnSelectItem(tabItem);
                this.Items.Remove(tabItem);
            }

            if (this.Items.Count > 0)
            {
                if (this.RightToLeft == RightToLeft.No)
                {
                    if (this.Items[tabIndex - 1] != null)
                    {
                        this.SelectedItem = this.Items[tabIndex - 1];
                    }
                    else
                    {
                        this.SelectedItem = this.Items.FirstVisible;
                    }
                }
                else
                {
                    if (this.Items[tabIndex + 1] != null)
                    {
                        this.SelectedItem = this.Items[tabIndex + 1];
                    }
                    else
                    {
                        this.SelectedItem = this.Items.LastVisible;
                    }
                }
            }
        }

        /// <summary>
        /// 	Get a <see cref="FATabStripItem" /> at provided point.
        /// 	If no item was found, returns null value.
        /// </summary>
        /// <param name="pt"> </param>
        /// <returns> </returns>
        public FATabStripItem GetTabItemByPoint(Point pt)
        {
            FATabStripItem item = null;
            bool found = false;

            for (int i = 0; i < this.Items.Count; i++)
            {
                FATabStripItem current = this.Items[i];

                if (current.StripRect.Contains(pt) && current.Visible && current.IsDrawn)
                {
                    item = current;
                    found = true;
                }

                if (found)
                {
                    break;
                }
            }

            return item;
        }

        /// <summary>
        /// 	Display items menu
        /// </summary>
        public virtual void ShowMenu()
        {
            if (this.menu.Visible == false && this.menu.Items.Count > 0)
            {
                if (this.RightToLeft == RightToLeft.No)
                {
                    this.menu.Show(this, new Point(this.menuGlyph.Bounds.Left, this.menuGlyph.Bounds.Bottom));
                }
                else
                {
                    this.menu.Show(this, new Point(this.menuGlyph.Bounds.Right, this.menuGlyph.Bounds.Bottom));
                }

                this.menuOpen = true;
            }
        }

        #endregion

        #region Internal

        internal void UnDrawAll()
        {
            for (int i = 0; i < this.Items.Count; i++)
            {
                this.Items[i].IsDrawn = false;
            }
        }

        internal void SelectItem(FATabStripItem tabItem)
        {
            tabItem.Dock = DockStyle.Fill;
            tabItem.Visible = true;
            tabItem.Selected = true;
        }

        internal void UnSelectItem(FATabStripItem tabItem)
        {
            //tabItem.Visible = false;
            tabItem.Selected = false;
        }

        #endregion

        #region Protected

        /// <summary>
        /// 	Fires <see cref="TabStripItemClosing" /> event.
        /// </summary>
        /// <param name="e"> </param>
        protected internal virtual void OnTabStripItemClosing(TabStripItemClosingEventArgs e)
        {
            if (this.TabStripItemClosing != null)
            {
                this.TabStripItemClosing(e);
            }
        }

        /// <summary>
        /// 	Fires <see cref="TabStripItemClosed" /> event.
        /// </summary>
        /// <param name="e"> </param>
        protected internal virtual void OnTabStripItemClosed(EventArgs e)
        {
            if (this.TabStripItemClosed != null)
            {
                this.TabStripItemClosed(this, e);
            }
        }

        /// <summary>
        /// 	Fires <see cref="MenuItemsLoading" /> event.
        /// </summary>
        /// <param name="e"> </param>
        protected virtual void OnMenuItemsLoading(HandledEventArgs e)
        {
            if (this.MenuItemsLoading != null)
            {
                this.MenuItemsLoading(this, e);
            }
        }

        /// <summary>
        /// 	Fires <see cref="MenuItemsLoaded" /> event.
        /// </summary>
        /// <param name="e"> </param>
        protected virtual void OnMenuItemsLoaded(EventArgs e)
        {
            if (this.MenuItemsLoaded != null)
            {
                this.MenuItemsLoaded(this, e);
            }
        }

        /// <summary>
        /// 	Fires <see cref="TabStripItemSelectionChanged" /> event.
        /// </summary>
        /// <param name="e"> </param>
        protected virtual void OnTabStripItemChanged(TabStripItemChangedEventArgs e)
        {
            if (this.TabStripItemSelectionChanged != null)
            {
                this.TabStripItemSelectionChanged(e);
            }
        }

        /// <summary>
        /// 	Loads menu items based on <see cref="FATabStripItem" />s currently added
        /// 	to this control.
        /// </summary>
        /// <param name="e"> </param>
        protected virtual void OnMenuItemsLoad(EventArgs e)
        {
            this.menu.RightToLeft = this.RightToLeft;
            this.menu.Items.Clear();

            for (int i = 0; i < this.Items.Count; i++)
            {
                FATabStripItem item = this.Items[i];
                if (!item.Visible)
                {
                    continue;
                }

                var tItem = new ToolStripMenuItem(item.Title);
                tItem.Tag = item;
                tItem.Image = item.Image;
                this.menu.Items.Add(tItem);
            }

            this.OnMenuItemsLoaded(EventArgs.Empty);
        }

        #endregion

        #region Overrides

        protected override void OnRightToLeftChanged(EventArgs e)
        {
            base.OnRightToLeftChanged(e);
            this.UpdateLayout();
            this.Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            this.SetDefaultSelected();
            Rectangle borderRc = this.ClientRectangle;
            borderRc.Width--;
            borderRc.Height--;

            if (this.RightToLeft == RightToLeft.No)
            {
                this.DEF_START_POS = 10;
            }
            else
            {
                this.DEF_START_POS = this.stripButtonRect.Right;
            }

            e.Graphics.DrawRectangle(SystemPens.ControlDark, borderRc);
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

            #region Draw Pages

            for (int i = 0; i < this.Items.Count; i++)
            {
                FATabStripItem currentItem = this.Items[i];
                if (!currentItem.Visible && !this.DesignMode)
                {
                    continue;
                }

                this.OnCalcTabPage(e.Graphics, currentItem);
                currentItem.IsDrawn = false;

                if (!this.AllowDraw(currentItem))
                {
                    continue;
                }

                this.OnDrawTabPage(e.Graphics, currentItem);
            }

            #endregion

            #region Draw UnderPage Line

            if (this.RightToLeft == RightToLeft.No)
            {
                if (this.Items.DrawnCount == 0 || this.Items.VisibleCount == 0)
                {
                    e.Graphics.DrawLine(
                        SystemPens.ControlDark,
                        new Point(0, DEF_HEADER_HEIGHT),
                        new Point(this.ClientRectangle.Width, DEF_HEADER_HEIGHT));
                }
                else if (this.SelectedItem != null && this.SelectedItem.IsDrawn)
                {
                    var end = new Point((int)this.SelectedItem.StripRect.Left - 9, DEF_HEADER_HEIGHT);
                    e.Graphics.DrawLine(SystemPens.ControlDark, new Point(0, DEF_HEADER_HEIGHT), end);
                    end.X += (int)this.SelectedItem.StripRect.Width + 10;
                    e.Graphics.DrawLine(
                        SystemPens.ControlDark, end, new Point(this.ClientRectangle.Width, DEF_HEADER_HEIGHT));
                }
            }
            else
            {
                if (this.Items.DrawnCount == 0 || this.Items.VisibleCount == 0)
                {
                    e.Graphics.DrawLine(
                        SystemPens.ControlDark,
                        new Point(0, DEF_HEADER_HEIGHT),
                        new Point(this.ClientRectangle.Width, DEF_HEADER_HEIGHT));
                }
                else if (this.SelectedItem != null && this.SelectedItem.IsDrawn)
                {
                    var end = new Point((int)this.SelectedItem.StripRect.Left, DEF_HEADER_HEIGHT);
                    e.Graphics.DrawLine(SystemPens.ControlDark, new Point(0, DEF_HEADER_HEIGHT), end);
                    end.X += (int)this.SelectedItem.StripRect.Width + 20;
                    e.Graphics.DrawLine(
                        SystemPens.ControlDark, end, new Point(this.ClientRectangle.Width, DEF_HEADER_HEIGHT));
                }
            }

            #endregion

            #region Draw Menu and Close Glyphs

            if (this.AlwaysShowMenuGlyph || this.Items.DrawnCount > this.Items.VisibleCount)
            {
                this.menuGlyph.DrawGlyph(e.Graphics);
            }

            if (this.AlwaysShowClose || (this.SelectedItem != null && this.SelectedItem.CanClose))
            {
                this.closeButton.DrawCross(e.Graphics);
            }

            #endregion
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if (e.Button != MouseButtons.Left)
            {
                return;
            }

            HitTestResult result = this.HitTest(e.Location);
            if (result == HitTestResult.MenuGlyph)
            {
                var args = new HandledEventArgs(false);
                this.OnMenuItemsLoading(args);

                if (!args.Handled)
                {
                    this.OnMenuItemsLoad(EventArgs.Empty);
                }

                this.ShowMenu();
            }
            else if (result == HitTestResult.CloseButton)
            {
                if (this.SelectedItem != null)
                {
                    var args = new TabStripItemClosingEventArgs(this.SelectedItem);
                    this.OnTabStripItemClosing(args);
                    if (!args.Cancel && this.SelectedItem.CanClose)
                    {
                        this.RemoveTab(this.SelectedItem);
                        this.OnTabStripItemClosed(EventArgs.Empty);
                    }
                }
            }
            else if (result == HitTestResult.TabItem)
            {
                FATabStripItem item = this.GetTabItemByPoint(e.Location);
                if (item != null)
                {
                    this.SelectedItem = item;
                }
            }

            this.Invalidate();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (this.menuGlyph.Bounds.Contains(e.Location))
            {
                this.menuGlyph.IsMouseOver = true;
                this.Invalidate(this.menuGlyph.Bounds);
            }
            else
            {
                if (this.menuGlyph.IsMouseOver && !this.menuOpen)
                {
                    this.menuGlyph.IsMouseOver = false;
                    this.Invalidate(this.menuGlyph.Bounds);
                }
            }

            if (this.closeButton.Bounds.Contains(e.Location))
            {
                this.closeButton.IsMouseOver = true;
                this.Invalidate(this.closeButton.Bounds);
            }
            else
            {
                if (this.closeButton.IsMouseOver)
                {
                    this.closeButton.IsMouseOver = false;
                    this.Invalidate(this.closeButton.Bounds);
                }
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            this.menuGlyph.IsMouseOver = false;
            this.Invalidate(this.menuGlyph.Bounds);

            this.closeButton.IsMouseOver = false;
            this.Invalidate(this.closeButton.Bounds);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (this.isIniting)
            {
                return;
            }

            this.UpdateLayout();
        }

        #endregion

        #region Private

        private bool AllowDraw(FATabStripItem item)
        {
            bool result = true;

            if (this.RightToLeft == RightToLeft.No)
            {
                if (item.StripRect.Right >= this.stripButtonRect.Width)
                {
                    result = false;
                }
            }
            else
            {
                if (item.StripRect.Left <= this.stripButtonRect.Left)
                {
                    return false;
                }
            }

            return result;
        }

        private void SetDefaultSelected()
        {
            if (this.selectedItem == null && this.Items.Count > 0)
            {
                this.SelectedItem = this.Items[0];
            }

            for (int i = 0; i < this.Items.Count; i++)
            {
                FATabStripItem itm = this.Items[i];
                itm.Dock = DockStyle.Fill;
            }
        }

        private void OnMenuItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            var clickedItem = (FATabStripItem)e.ClickedItem.Tag;
            this.SelectedItem = clickedItem;
        }

        private void OnMenuVisibleChanged(object sender, EventArgs e)
        {
            if (this.menu.Visible == false)
            {
                this.menuOpen = false;
            }
        }

        private void OnCalcTabPage(Graphics g, FATabStripItem currentItem)
        {
            Font currentFont = this.Font;
            if (currentItem == this.SelectedItem)
            {
                currentFont = new Font(this.Font, FontStyle.Bold);
            }

            SizeF textSize = g.MeasureString(currentItem.Title, currentFont, new SizeF(200, 10), this.sf);
            textSize.Width += 20;

            if (this.RightToLeft == RightToLeft.No)
            {
                var buttonRect = new RectangleF(this.DEF_START_POS, 3, textSize.Width, 17);
                currentItem.StripRect = buttonRect;
                this.DEF_START_POS += (int)textSize.Width;
            }
            else
            {
                var buttonRect = new RectangleF(this.DEF_START_POS - textSize.Width + 1, 3, textSize.Width - 1, 17);
                currentItem.StripRect = buttonRect;
                this.DEF_START_POS -= (int)textSize.Width;
            }
        }

        private void OnDrawTabPage(Graphics g, FATabStripItem currentItem)
        {
            bool isFirstTab = this.Items.IndexOf(currentItem) == 0;
            Font currentFont = this.Font;

            if (currentItem == this.SelectedItem)
            {
                currentFont = new Font(this.Font, FontStyle.Bold);
            }

            SizeF textSize = g.MeasureString(currentItem.Title, currentFont, new SizeF(200, 10), this.sf);
            textSize.Width += 20;
            RectangleF buttonRect = currentItem.StripRect;

            var path = new GraphicsPath();
            LinearGradientBrush brush;
            int mtop = 3;

            #region Draw Not Right-To-Left Tab

            if (this.RightToLeft == RightToLeft.No)
            {
                if (currentItem == this.SelectedItem || isFirstTab)
                {
                    path.AddLine(
                        buttonRect.Left - 10,
                        buttonRect.Bottom - 1,
                        buttonRect.Left + (buttonRect.Height / 2) - 4,
                        mtop + 4);
                }
                else
                {
                    path.AddLine(
                        buttonRect.Left,
                        buttonRect.Bottom - 1,
                        buttonRect.Left,
                        buttonRect.Bottom - (buttonRect.Height / 2) - 2);
                    path.AddLine(
                        buttonRect.Left,
                        buttonRect.Bottom - (buttonRect.Height / 2) - 3,
                        buttonRect.Left + (buttonRect.Height / 2) - 4,
                        mtop + 3);
                }

                path.AddLine(buttonRect.Left + (buttonRect.Height / 2) + 2, mtop, buttonRect.Right - 3, mtop);
                path.AddLine(buttonRect.Right, mtop + 2, buttonRect.Right, buttonRect.Bottom - 1);
                path.AddLine(buttonRect.Right - 4, buttonRect.Bottom - 1, buttonRect.Left, buttonRect.Bottom - 1);
                path.CloseFigure();

                if (currentItem == this.SelectedItem)
                {
                    brush = new LinearGradientBrush(
                        buttonRect, SystemColors.ControlLightLight, SystemColors.Window, LinearGradientMode.Vertical);
                }
                else
                {
                    brush = new LinearGradientBrush(
                        buttonRect, SystemColors.ControlLightLight, SystemColors.Control, LinearGradientMode.Vertical);
                }

                g.FillPath(brush, path);
                g.DrawPath(SystemPens.ControlDark, path);

                if (currentItem == this.SelectedItem)
                {
                    g.DrawLine(
                        new Pen(brush),
                        buttonRect.Left - 9,
                        buttonRect.Height + 2,
                        buttonRect.Left + buttonRect.Width - 1,
                        buttonRect.Height + 2);
                }

                var textLoc = new PointF(
                    buttonRect.Left + buttonRect.Height - 4,
                    buttonRect.Top + (buttonRect.Height / 2) - (textSize.Height / 2) - 3);
                RectangleF textRect = buttonRect;
                textRect.Location = textLoc;
                textRect.Width = buttonRect.Width - (textRect.Left - buttonRect.Left) - 4;
                textRect.Height = textSize.Height + currentFont.Size / 2;

                if (currentItem == this.SelectedItem)
                {
                    //textRect.Y -= 2;
                    g.DrawString(currentItem.Title, currentFont, new SolidBrush(this.ForeColor), textRect, this.sf);
                }
                else
                {
                    g.DrawString(currentItem.Title, currentFont, new SolidBrush(this.ForeColor), textRect, this.sf);
                }
            }

            #endregion

            #region Draw Right-To-Left Tab

            if (this.RightToLeft == RightToLeft.Yes)
            {
                if (currentItem == this.SelectedItem || isFirstTab)
                {
                    path.AddLine(
                        buttonRect.Right + 10,
                        buttonRect.Bottom - 1,
                        buttonRect.Right - (buttonRect.Height / 2) + 4,
                        mtop + 4);
                }
                else
                {
                    path.AddLine(
                        buttonRect.Right,
                        buttonRect.Bottom - 1,
                        buttonRect.Right,
                        buttonRect.Bottom - (buttonRect.Height / 2) - 2);
                    path.AddLine(
                        buttonRect.Right,
                        buttonRect.Bottom - (buttonRect.Height / 2) - 3,
                        buttonRect.Right - (buttonRect.Height / 2) + 4,
                        mtop + 3);
                }

                path.AddLine(buttonRect.Right - (buttonRect.Height / 2) - 2, mtop, buttonRect.Left + 3, mtop);
                path.AddLine(buttonRect.Left, mtop + 2, buttonRect.Left, buttonRect.Bottom - 1);
                path.AddLine(buttonRect.Left + 4, buttonRect.Bottom - 1, buttonRect.Right, buttonRect.Bottom - 1);
                path.CloseFigure();

                if (currentItem == this.SelectedItem)
                {
                    brush = new LinearGradientBrush(
                        buttonRect, SystemColors.ControlLightLight, SystemColors.Window, LinearGradientMode.Vertical);
                }
                else
                {
                    brush = new LinearGradientBrush(
                        buttonRect, SystemColors.ControlLightLight, SystemColors.Control, LinearGradientMode.Vertical);
                }

                g.FillPath(brush, path);
                g.DrawPath(SystemPens.ControlDark, path);

                if (currentItem == this.SelectedItem)
                {
                    g.DrawLine(
                        new Pen(brush),
                        buttonRect.Right + 9,
                        buttonRect.Height + 2,
                        buttonRect.Right - buttonRect.Width + 1,
                        buttonRect.Height + 2);
                }

                var textLoc = new PointF(
                    buttonRect.Left + 2, buttonRect.Top + (buttonRect.Height / 2) - (textSize.Height / 2) - 2);
                RectangleF textRect = buttonRect;
                textRect.Location = textLoc;
                textRect.Width = buttonRect.Width - (textRect.Left - buttonRect.Left) - 10;
                textRect.Height = textSize.Height + currentFont.Size / 2;

                if (currentItem == this.SelectedItem)
                {
                    textRect.Y -= 1;
                    g.DrawString(currentItem.Title, currentFont, new SolidBrush(this.ForeColor), textRect, this.sf);
                }
                else
                {
                    g.DrawString(currentItem.Title, currentFont, new SolidBrush(this.ForeColor), textRect, this.sf);
                }

                //g.FillRectangle(Brushes.Red, textRect);
            }

            #endregion

            currentItem.IsDrawn = true;
        }

        private void UpdateLayout()
        {
            if (this.RightToLeft == RightToLeft.No)
            {
                this.sf.Trimming = StringTrimming.EllipsisCharacter;
                this.sf.FormatFlags |= StringFormatFlags.NoWrap;
                this.sf.FormatFlags &= StringFormatFlags.DirectionRightToLeft;

                this.stripButtonRect = new Rectangle(0, 0, this.ClientSize.Width - DEF_GLYPH_WIDTH - 2, 10);
                this.menuGlyph.Bounds = new Rectangle(this.ClientSize.Width - DEF_GLYPH_WIDTH, 2, 16, 16);
                this.closeButton.Bounds = new Rectangle(this.ClientSize.Width - 20, 2, 16, 16);
            }
            else
            {
                this.sf.Trimming = StringTrimming.EllipsisCharacter;
                this.sf.FormatFlags |= StringFormatFlags.NoWrap;
                this.sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft;

                this.stripButtonRect = new Rectangle(
                    DEF_GLYPH_WIDTH + 2, 0, this.ClientSize.Width - DEF_GLYPH_WIDTH - 15, 10);
                this.closeButton.Bounds = new Rectangle(4, 2, 16, 16); //ClientSize.Width - DEF_GLYPH_WIDTH, 2, 16, 16);
                this.menuGlyph.Bounds = new Rectangle(20 + 4, 2, 16, 16); //this.ClientSize.Width - 20, 2, 16, 16);
            }

            this.DockPadding.Top = DEF_HEADER_HEIGHT + 1;
            this.DockPadding.Bottom = 1;
            this.DockPadding.Right = 1;
            this.DockPadding.Left = 1;
        }

        private void OnCollectionChanged(object sender, CollectionChangeEventArgs e)
        {
            var itm = (FATabStripItem)e.Element;

            if (e.Action == CollectionChangeAction.Add)
            {
                this.Controls.Add(itm);
                this.OnTabStripItemChanged(new TabStripItemChangedEventArgs(itm, FATabStripItemChangeTypes.Added));
            }
            else if (e.Action == CollectionChangeAction.Remove)
            {
                this.Controls.Remove(itm);
                this.OnTabStripItemChanged(new TabStripItemChangedEventArgs(itm, FATabStripItemChangeTypes.Removed));
            }
            else
            {
                this.OnTabStripItemChanged(new TabStripItemChangedEventArgs(itm, FATabStripItemChangeTypes.Changed));
            }

            this.UpdateLayout();
            this.Invalidate();
        }

        #endregion

        #endregion

        #region Ctor

        public FATabStrip()
        {
            this.BeginInit();

            this.SetStyle(ControlStyles.ContainerControl, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.Selectable, true);

            this.items = new FATabStripItemCollection();
            this.items.CollectionChanged += this.OnCollectionChanged;
            base.Size = new Size(350, 200);

            this.menu = new ContextMenuStrip();
            this.menu.Renderer = this.ToolStripRenderer;
            this.menu.ItemClicked += this.OnMenuItemClicked;
            this.menu.VisibleChanged += this.OnMenuVisibleChanged;

            this.menuGlyph = new FATabStripMenuGlyph(this.ToolStripRenderer);
            this.closeButton = new FATabStripCloseButton(this.ToolStripRenderer);
            this.Font = defaultFont;
            this.sf = new StringFormat();

            this.EndInit();

            this.UpdateLayout();
        }

        #endregion

        #region Props

        [DefaultValue(null)]
        [RefreshProperties(RefreshProperties.All)]
        public FATabStripItem SelectedItem
        {
            get
            {
                return this.selectedItem;
            }
            set
            {
                if (this.selectedItem == value)
                {
                    return;
                }

                if (value == null && this.Items.Count > 0)
                {
                    FATabStripItem itm = this.Items[0];
                    if (itm.Visible)
                    {
                        this.selectedItem = itm;
                        this.selectedItem.Selected = true;
                        this.selectedItem.Dock = DockStyle.Fill;
                    }
                }
                else
                {
                    this.selectedItem = value;
                }

                foreach (FATabStripItem itm in this.Items)
                {
                    if (itm == this.selectedItem)
                    {
                        this.SelectItem(itm);
                        itm.Dock = DockStyle.Fill;
                        itm.Show();
                    }
                    else
                    {
                        this.UnSelectItem(itm);
                        itm.Hide();
                    }
                }

                this.SelectItem(this.selectedItem);
                this.Invalidate();

                if (!this.selectedItem.IsDrawn)
                {
                    this.Items.MoveTo(0, this.selectedItem);
                    this.Invalidate();
                }

                this.OnTabStripItemChanged(
                    new TabStripItemChangedEventArgs(this.selectedItem, FATabStripItemChangeTypes.SelectionChanged));
            }
        }

        [DefaultValue(true)]
        public bool AlwaysShowMenuGlyph
        {
            get
            {
                return this.alwaysShowMenuGlyph;
            }
            set
            {
                if (this.alwaysShowMenuGlyph == value)
                {
                    return;
                }

                this.alwaysShowMenuGlyph = value;
                this.Invalidate();
            }
        }

        [DefaultValue(true)]
        public bool AlwaysShowClose
        {
            get
            {
                return this.alwaysShowClose;
            }
            set
            {
                if (this.alwaysShowClose == value)
                {
                    return;
                }

                this.alwaysShowClose = value;
                this.Invalidate();
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public FATabStripItemCollection Items
        {
            get
            {
                return this.items;
            }
        }

        [DefaultValue(typeof(Size), "350,200")]
        public new Size Size
        {
            get
            {
                return base.Size;
            }
            set
            {
                if (base.Size == value)
                {
                    return;
                }

                base.Size = value;
                this.UpdateLayout();
            }
        }

        /// <summary>
        /// 	DesignerSerializationVisibility
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new ControlCollection Controls
        {
            get
            {
                return base.Controls;
            }
        }

        #endregion

        #region ShouldSerialize

        public bool ShouldSerializeFont()
        {
            return this.Font != null && !this.Font.Equals(defaultFont);
        }

        public bool ShouldSerializeSelectedItem()
        {
            return true;
        }

        public bool ShouldSerializeItems()
        {
            return this.items.Count > 0;
        }

        public new void ResetFont()
        {
            this.Font = defaultFont;
        }

        #endregion

        #region ISupportInitialize Members

        public void BeginInit()
        {
            this.isIniting = true;
        }

        public void EndInit()
        {
            this.isIniting = false;
        }

        #endregion

        #region IDisposable

        ///<summary>
        ///	Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        ///</summary>
        ///<filterpriority>2</filterpriority>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.items.CollectionChanged -= this.OnCollectionChanged;
                this.menu.ItemClicked -= this.OnMenuItemClicked;
                this.menu.VisibleChanged -= this.OnMenuVisibleChanged;

                foreach (FATabStripItem item in this.items)
                {
                    if (item != null && !item.IsDisposed)
                    {
                        item.Dispose();
                    }
                }

                if (this.menu != null && !this.menu.IsDisposed)
                {
                    this.menu.Dispose();
                }

                if (this.sf != null)
                {
                    this.sf.Dispose();
                }
            }

            base.Dispose(disposing);
        }

        #endregion
    }
}